/*
 Copyright (c) [Year] [name of copyright holder]
 [Software Name] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan
 PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.
*/

#include "PasteModule.hpp"

/**
 * @brief 图像处理函数
 *
 * 本函数负责对指定目录下的图像进行处理，可以根据需求选择自动或手动调整图像。
 * 以及基于一个基础编号，对图像进行统一编码。
 * 通过它来实现图像的批量处理和个性化处理需求。
 *
 * @param dir 图像文件所在的目录路径
 * @param imgExt 图像文件的扩展名(带点: .jpg,
 * .bmp等)，用于识别和处理特定类型的图像文件
 * @param baseNum 基础编号，用于在处理过程中对图像进行统一命名编号
 * @param isManual 是否需要手动调整图像，true为手动，false为自动
 * @return int 返回图像处理的结果代码，0表示成功，非0表示失败
 */
int PasteModule::ImgProcess(const std::string &dir, const std::string &imgExt, const int baseNum, const bool isManual) {
    // 检查 OpenCV 是否正确初始化
    if (!cv::getBuildInformation().empty()) {
        std::cout << "OpenCV已正确初始化。" << std::endl;
    } else {
        std::cerr << "错误: OpenCV初始化失败。任意键退出程序!" << std::endl;
        std::cin.get();
        exit(0);
    }

    // 提示用户将要处理的目录，并请求确认
    std::cout << "程序要对[" << dir << "]目录下的[" << imgExt << "]图片进行" << (isManual ? "手动调整," : "自动")
              << "添加标签,切割操作!" << std::endl;
    std::cout << "确认请输入 1 或 y ,其键退出程序" << std::endl;
    std::string input;
    std::cin >> input;
    if (input != "1" && input != "y") {
        return 1;
    }

    // 获取指定目录下特定扩展名的文件
    std::vector<fs::path> files; // 图片信息文本列表
    try {
        PasteIO::GetFiles(files, dir, ".txt", false);
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
        PasteIO::WriteError(e.what());
    }

    // 定义保存图像的目录和扩展名
    std::string saveDir = dir + "saveImg/"; // 图片保存目录
    // 尝试创建保存图像所需的目录，并创建图像显示窗口
    try {
        PasteIO::VerifyDir(saveDir + "bmp1/", true);
        PasteIO::VerifyDir(saveDir + "jpg1/", true);
        // PasteIO::VerifyDir(saveDir + "bmp2/", true);
        // PasteIO::VerifyDir(saveDir + "jpg2/", true);
        PasteIO::VerifyDir(saveDir + "split/", true);

        // 创建并命名显示图像的窗口
        cv::namedWindow("PasteAdjustImg", cv::WINDOW_FREERATIO);
    } catch (const std::exception &e) {
        PasteIO::WriteError("[ImgProcess] " + std::string(e.what()));
    }

    // 遍历文件列表，处理每个文件
    for (auto &p : files) {
        std::string txtFilePath = p.string();         // 图片信息文本文件路径
        p.replace_extension();                        // 去掉扩展名
        std::string fileName = p.filename().string(); // 图片文件名(不带扩展名)

        std::string imgFilePath = p.string() + imgExt;                // 图片文件路径
        std::string bmpPath1 = saveDir + "bmp1/" + fileName + ".bmp"; // 没处理基数的图片保存路径
        std::string jpgPath1 = saveDir + "jpg1/" + fileName + ".jpg"; // 没处理基数的压缩图片保存路径
        // std::string bmpPath2 = saveDir + "bmp2/" + fileName + ".bmp";
        // std::string jpgPath2 = saveDir + "jpg2/" + fileName + ".jpg";
        std::string splitDir = saveDir + "split/"; // 分割图片保存目录

        try {
            std::vector<std::string> textList;          // 保存图片信息文本的字符串列表
            PasteIO::TextToList(textList, txtFilePath); // 从图片信息文本中读取字符串
            std::string str1 = textList[0];             // 第一行字符串，行*列
            textList.erase(textList.begin());           // 删除第一行字符串

            int rows = 0;                              // 行数
            int cols = 0;                              // 列数
            PasteConvert::StrTo2Num(str1, rows, cols); // 将行*列字符串转换为行和列

            // 验证行列值是否在有效范围内
            if (rows < 1 || rows > 100 || cols < 1 || cols > 100) {
                std::cout << txtFilePath << "文本第一行 行列数值不在1~100之间" << std::endl;
                continue;
            }

            // 创建PasteAdjustImg对象
            PasteAdjustImg ai(imgFilePath, rows, cols, cv::Scalar(0, 255, 0), 10, 8, "PasteAdjustImg");

            if (isManual) { // 手工调整图像边缘
                if (ai.TrimEdge()) {
                    return 1;
                }
            }

            // 调整图像并绘制文本
            // 设置网格信息
            ai.ChangeGrid(rows, cols, cv::Scalar(100, 100, 100), 10);

            if (isManual) { // 移除空白区域
                if (ai.RemoveBlank()) {
                    return 1;
                }
            }

            //------以下是原始标签图片------
            // 绘制文本标签
            if (ai.DrawASKII(textList, true)) {
                return 1;
            }
            ai.SaveImg(bmpPath1); // 保存原始标签图片
            ai.SaveImg(jpgPath1); // 保存原始标签压缩图片
            //------以上是原始标签图片------

            //------以下是统一编号标签图片------
            // 编号处理成统一编号，转换为十六进制
            for (int i = 0; i < textList.size(); i += 2) {
                std::string numStr = textList[i]; // 单元格图像编号
                if (numStr.empty() || numStr == "0") {
                    continue;
                }
                int num = std::stoi(numStr);       // 字符串转为整数
                num = baseNum - num;               // 基数减去单元格图像编号
                char buffer[11];                   //  存储转换后的十六进制字符串
                std::sprintf(buffer, "%X", num);   //  将整数转换为十六进制字符串
                textList[i] = std::string(buffer); // 更改处理过的单元格图像编号
            }

            // 绘制文本标签
            if (ai.DrawASKII(textList, true)) {
                return 1;
            }
            // ai.SaveImg(bmpPath2);
            // ai.SaveImg(jpgPath2);

            std::vector<std::string> fileNameList;     // 保存文件名列表
            fileNameList.reserve(textList.size() / 2); // 分配足够的空间
            // 将文本列表中处理过的单元格图像编号添加到fileNameList中作为文件名
            for (int i = 0; i < textList.size(); i += 2) {
                fileNameList.push_back(textList[i]);
            }
            ai.SplitSaveImg(fileNameList, splitDir, ".jpg");
            //------以上是统一编号标签图片------
        } catch (const std::exception &e) {
            PasteIO::WriteError("[ImgProcess] " + std::string(e.what()));
        }
    }
    // 关闭所有OpenCV创建的窗口
    cv::destroyAllWindows();

    return 0;
}

/**
 * @brief 保存图片信息
 *
 * 该函数用于将分割后的图片信息保存到数据库中。它根据给定的连接字符串、目录、图片扩展名，
 * 以及相关的数字ID，执行保存操作。这个函数主要是为了解决图片信息的持久化问题，
 * 确保图片能够被正确地记录和追踪。
 *
 * @param connStr 数据库连接字符串，用于建立与数据库的连接
 * @param dir 图片保存的目录路径，确保路径存在且有写入权限
 * @param imgExt 图片扩展名，用于指定处理图片的格式
 * @param baseNum 基础编号，用于生成图片的统一唯一标识
 * @param categoryID 类别ID，用于对图片进行分类管理
 * @param materialID 材质ID，关联到使用的材质记录
 * @return int 返回操作结果，0表示成功，非0表示失败
 */
int PasteModule::SaveImgInfo(const PasteDB::ConnStruct &connStr, const std::string &dir, const std::string &imgExt,
                             const int baseNum, const int categoryID, const int materialID) {
    // 提示用户将要处理的目录，并请求确认
    std::cout << "程序要对[" << dir << "]目录下的[" << imgExt << "]图片进行重命名成UUID并保存到数据库中!" << std::endl;
    std::cout << "确认请输入 1 或 y ,其键退出程序" << std::endl;
    std::string input;
    std::cin >> input;
    if (input != "1" && input != "y") {
        return 1;
    }

    // 获取指定目录下特定扩展名的文件
    std::vector<fs::path> files; // 图片信息文本列表
    try {
        PasteIO::GetFiles(files, dir, ".txt", false);
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
        PasteIO::WriteError(e.what());
    }

    std::stringstream repeatSS;                   // 重复项提示字符串
    std::unordered_map<int, int> imgNumMap;       // 图片编号无序列表(检测重名图片)
    std::vector<PasteDB::ImageStruct> imginfoArr; // 图片信息列表
    imgNumMap.reserve(files.size() * 22);         // 分配足够的空间
    imginfoArr.reserve(files.size() * 22);        // 平均下来每张图有22个数据大多是5*4的,5*5也有,更少的也有
    try {
        // 遍历每个文件，解析并处理数据
        for (auto &p : files) {
            // 获取文件路径字符串
            std::string txtFilePath = p.string(); // 文件路径
            // 大图片文件名后面加序号标注每个小图片所在位置
            std::string fileName = p.replace_extension("").filename().string() + "_";

            std::vector<std::string> strList;          // 文件以行为单位读取的字符串列表
            strList.reserve(51);                       // 平均下来每张图有41~51个数据
            PasteIO::TextToList(strList, txtFilePath); // 读取文件内容到strList

            // 移除第一行，因为第一行是 行x列 信息，不需要处理，如果文件为空则跳过
            if (!strList.empty())
                strList.erase(strList.begin());
            else {
                continue;
            } // 跳过空文件

            // 成对处理文本数据，每两个一组
            int count = strList.size(); // 文本列表中的元素数量
            for (int i = 0; i < count; i += 2) {
                int tmpInt0 = 0; // 编号
                int tmpInt1 = 0; // 尺寸1(um)
                int tmpInt2 = 0; // 尺寸2(um)
                int tmpInt4 = 0; // 质量(mg)

                std::string firstStr = strList[i]; // 第一行字符串,图片编号
                if (firstStr.empty() || firstStr == "0") {
                    continue;
                } // 跳过无效数据

                try {
                    // 基础编号减去 图片编号字符串转换为整数
                    tmpInt0 = baseNum - std::stoi(firstStr);
                } catch (const std::exception &e) {
                    throw std::runtime_error("图片编号解析错误" + firstStr + " - " + e.what());
                }

                std::string tmpStr1 = "0"; // 尺寸1Str
                std::string tmpStr4 = "0"; // 质量Str
                // 解析尺寸和质量信息
                // 第二行字符串,  "尺寸mm 质量g"  或 "尺寸1x尺寸2mm 质量g" 或 "质量g"
                std::string secondStr = strList[i + 1];
                // 用空格符号" " 分割尺寸和质量
                size_t index = secondStr.find(" ");
                if (index == std::string::npos) {
                    tmpStr4 = secondStr; // 只有质量Str
                } else {
                    tmpStr1 = secondStr.substr(0, index);  // 尺寸1Str
                    tmpStr4 = secondStr.substr(index + 1); // 质量Str
                }
                // 用"x"符号 分割尺寸1和尺寸2
                index = tmpStr1.find("x");
                try {
                    // 双尺寸
                    if (index != std::string::npos) {
                        std::string tmpStr2 = tmpStr1.substr(index + 1); // 尺寸2Str
                        tmpStr1 = tmpStr1.substr(0, index);

                        tmpInt1 = static_cast<int>(std::stod(tmpStr1) * 1000);
                        tmpInt2 = static_cast<int>(std::stod(tmpStr2) * 1000);
                    } else // 单尺寸
                    {
                        tmpInt1 = static_cast<int>(std::stod(tmpStr1) * 1000);
                    }
                    tmpInt4 = static_cast<int>(std::stod(tmpStr4) * 1000);
                } catch (const std::exception &e) {
                    throw std::runtime_error("图片尺寸质量解析错误" + secondStr + " - " + e.what());
                }

                // 图片信息结构体
                PasteDB::ImageStruct tmpImgInfo;
                std::string uuid, uuid2;
                PasteConvert::GenerateUUID(uuid);
                uuid2 = uuid.substr(0, 8) + uuid.substr(9, 4) + uuid.substr(14, 4) + uuid.substr(19, 4) + uuid.substr(24, 12);

                constexpr size_t size_ID0 = sizeof(tmpImgInfo.ID0) - 1;
                strncpy(tmpImgInfo.ID0, uuid.c_str(), size_ID0);
                tmpImgInfo.ID0[size_ID0] = '\0';

                constexpr size_t size_ID1 = sizeof(tmpImgInfo.ID1) - 1;
                strncpy(tmpImgInfo.ID1, uuid2.c_str(), size_ID1);
                tmpImgInfo.ID1[size_ID1] = '\0';

                tmpImgInfo.Int0 = tmpInt0;
                tmpImgInfo.Int1 = tmpInt1;
                tmpImgInfo.Int2 = tmpInt2;
                tmpImgInfo.Int3 = 0;
                tmpImgInfo.Int4 = tmpInt4;
                tmpImgInfo.Int5 = 0;
                tmpImgInfo.Int6 = categoryID;
                tmpImgInfo.Int7 = materialID;
                std::string tmpStr = fileName + std::to_string(i / 2);

                constexpr size_t size_Str1 = sizeof(tmpImgInfo.Str1) - 1;
                strncpy(tmpImgInfo.Str1, tmpStr.c_str(), size_Str1);
                tmpImgInfo.Str1[size_Str1] = '\0';

                // 时间用数据库自动填充的时间
                // auto now = std::chrono::system_clock::now() + std::chrono::hours(8);
                // std::string timeStr = std::format("{:%Y-%m-%d %H:%M:%S}", now);
                // constexpr size_t size_Time0 = sizeof(tmpImgInfo.Time0) - 1;
                // strncpy(tmpImgInfo.Time0, timeStr.c_str(), size_Time0);
                // tmpImgInfo.Time0[size_Time0] = '\0';

                // 判断是否已经存在该编号的图片信息
                auto pos = imgNumMap.find(tmpImgInfo.Int0);
                if (pos == imgNumMap.end()) {
                    // 如果不存在，则将图片信息添加到列表中
                    // 记录图片序号(imginfoArr中没添加当前信息的个数就是当前信息的序号)
                    imgNumMap[tmpImgInfo.Int0] = imginfoArr.size();
                    imginfoArr.emplace_back(tmpImgInfo); // 添加图片信息
                } else {
                    // 如果存在，则将文件名添加到重复项提示字符串中
                    tmpStr = std::string(tmpImgInfo.Str1) + " & " + imginfoArr[pos->second].Str1;

                    constexpr size_t size_Str1 = sizeof(tmpImgInfo.Str1) - 1;
                    strncpy(tmpImgInfo.Str1, tmpStr.c_str(), size_Str1);
                    tmpImgInfo.Str1[size_Str1] = '\0';

                    // 记录重复项提示字符串
                    repeatSS << std::to_string(baseNum - tmpImgInfo.Int0) << " ~ " << tmpImgInfo.Str1 << "\n";
                }
            }
        }

        // 修改图片文件名
        fs::path oldPath = dir + "1" + imgExt; // 原始文件名(这里的1后续会改成正确的文件名)
        fs::path newPath = dir + "1" + imgExt; // 新文件名(这里的1后续会改成正确的文件名)
        for (const auto &item : imginfoArr) {
            // 原始文件名
            std::stringstream ss;
            ss << std::hex << item.Int0 << imgExt;
            std::string imgFileName = ss.str();
            oldPath.replace_filename(imgFileName);

            // 新文件名
            imgFileName = std::string(item.ID1) + imgExt;
            newPath.replace_filename(imgFileName);

            if (fs::exists(oldPath)) {
                fs::rename(oldPath, newPath);
            } else {
                std::cout << "文件不存在：" << oldPath << std::endl;
                std::cout << "请自行添加图片文件：" << newPath << std::endl;
            }
        }

        // 初始化数据库连接
        PasteDB pdb = PasteDB(connStr);
        pdb.ImageListInsert(imginfoArr, false);
        // 输出重复项提示字符串
        PasteIO::SaveStringToFile(repeatSS, dir + "repeat.txt");
        std::cout << "重复信息已保存到" << dir << "repeat.txt" << std::endl;
    } catch (const std::exception &e) {
        PasteIO::WriteError("[SaveImgInfo]: " + std::string(e.what()));
    }
    return 0;
}

// 产品信息处理
int PasteModule::ProductProcess(const PasteDB::ConnStruct &connStr, const std::string &path) {
    // 定义一个lambda函数CompareStr，用于比较两个字符串并根据比较结果更新结果字符串
    auto CompareStr = [](std::string &originalStr, const std::string &compareStr, std::string &resultStr,
                         const std::string &tagStr) {
        // 如果compareStr为空或与originalStr相同，则无需进行比较，直接返回
        if (compareStr.empty() || originalStr == compareStr)
            return;

        // 如果originalStr为空，则将其赋值为compareStr
        if (originalStr.empty())
            originalStr = compareStr;
        else {
            // 构造一个临时字符串，格式为tagStr:compareStr
            std::string tempStr = tagStr + compareStr;

            // 如果临时字符串不为空且在resultStr中找不到，则将其追加到resultStr中
            if (!tempStr.empty() && resultStr.find(tempStr) == std::string::npos) {
                resultStr += tempStr;
            }
        }
    };

    // 定义一个lambda函数，用于比较两个整数并根据比较结果更新结果字符串
    auto CompareInt = [](int &originalInt, const int &compareInt, std::string &resultStr, const std::string &tagStr) {
        // 如果比较的整数为0或两个整数相等，则无需进行后续操作，直接返回
        if (0 == compareInt || originalInt == compareInt)
            return;

        // 如果原始整数为0，则将其值更新为比较的整数
        if (0 == originalInt)
            originalInt = compareInt;
        else {
            // 构造一个临时字符串，包含标签和比较的整数的字符串表示
            std::string tempStr = tagStr + std::to_string(compareInt);

            // 如果临时字符串非空且结果字符串中不包含此临时字符串，则将其追加到结果字符串中
            if (!tempStr.empty() && resultStr.find(tempStr) == std::string::npos) {
                resultStr += tempStr;
            }
        }
    };

    try {
        std::vector<std::string> strList;   // 文件以行为单位读取的字符串列表
        PasteIO::TextToList(strList, path); // 读取文件内容到strList

        std::stringstream dataConflict;             // 数据冲突
        std::stringstream noCategoryData;           // 没有类别的数据
        std::unordered_map<int, int> productNumMap; // 产品编号无序列表(检测重名产品)
        std::vector<ProductInfo> productInfoList;   // 产品信息列表
        productNumMap.reserve(strList.size());      // 分配足够的空间
        productInfoList.reserve(strList.size());    // 分配足够的空间

        int index = 0;
        // 遍历每个文件，解析并处理数据
        for (const auto str : strList) {
            if (++index % 1000 == 0) {
                std::cout << "处理第" << index << "条数据" << std::endl;
            }
            if (str.empty()) {
                continue;
            }
            // 类似数据格式
            // Int0	Int1	Int2	Int5	Str0	Str1	Str2	Str3	Time0
            // 模号 尺寸1   尺寸2   类别     客户    开发团队   关键字  备注    时间
            std::stringstream ss(str);
            std::string token;
            ProductInfo productInfo;
            // 模号
            if (!(std::getline(ss, token, '\t') && !token.empty())) {
                continue;
            }
            productInfo.Int0 = std::stoi(token);
            // 尺寸1
            if (!(std::getline(ss, token, '\t') && !token.empty())) {
                continue;
            }
            productInfo.Int1 = std::stoi(token);
            // 尺寸2
            if (!(std::getline(ss, token, '\t') && !token.empty())) {
                continue;
            }
            productInfo.Int2 = std::stoi(token);
            // 类别
            if (!(std::getline(ss, token, '\t') && !token.empty())) {
                continue;
            }
            productInfo.Int6 = std::stoi(token);

            // 客户
            if (!std::getline(ss, productInfo.ID2, '\t')) {
                continue;
            }

            // 开发团队
            if (!std::getline(ss, productInfo.ID1, '\t')) {
                continue;
            }

            // 关键字
            if (!std::getline(ss, productInfo.Str5, '\t')) {
                continue;
            }

            // 备注
            if (!std::getline(ss, productInfo.Str6, '\t')) {
                continue;
            }

            // 时间
            if (!std::getline(ss, productInfo.Time0, '\t')) {
                continue;
            }

            // 判断类别是否为0，如果为0，则跳过该条数据
            if (productInfo.Int6 == 0) {
                noCategoryData << str << std::endl;
                continue;
            }

            // 判断是否已经存在该ID的产品信息
            auto pos = productNumMap.find(productInfo.Int0);
            if (pos == productNumMap.end()) {
                // 如果ID不存在，则插入该ID对应的产品信息
                // 记录产品序号(productInfoArr中没添加当前信息的个数就是当前信息的序号)
                productNumMap[productInfo.Int0] = productInfoList.size();
                productInfoList.emplace_back(productInfo); // 添加产品信息
            } else {
                int index = pos->second;
                // 如果ID存在
                // 类型相同 合并信息
                if (productInfoList[index].Int6 == productInfo.Int6) {
                    // 尺寸1
                    CompareInt(productInfoList[index].Int1, productInfo.Int1, productInfoList[index].Str4, "尺寸1:");
                    // 尺寸2
                    CompareInt(productInfoList[index].Int2, productInfo.Int2, productInfoList[index].Str4, "尺寸2:");
                    // 客户
                    if (productInfoList[index].ID2.empty() && !productInfo.ID2.empty()) {
                        productInfoList[index].ID2 = productInfo.ID2;
                    }
                    // 开发团队
                    CompareStr(productInfoList[index].ID1, productInfo.ID1, productInfoList[index].Str4, "开发:");
                    // 关键字
                    CompareStr(productInfoList[index].Str5, productInfo.Str5, productInfoList[index].Str4, "关键字:");
                    // 备注
                    CompareStr(productInfoList[index].Str6, productInfo.Str6, productInfoList[index].Str6, " ");

                } else // 类型不同
                {
                    // 记录数据冲突
                    dataConflict << str << std::endl;
                }
            }
        }
        std::cout << "共处理了" << index << "条数据" << std::endl;
        PasteIO::SaveStringToFile(dataConflict, "D:/dataConflict.txt");
        PasteIO::SaveStringToFile(noCategoryData, "D:/noCategoryData.txt");

        std::cout << "数据冲突记录保存到D:/dataConflict.txt" << std::endl;

        int count = productInfoList.size();
        std::cout << count << std::endl;

        std::vector<PasteDB::ProductStruct> productInfoArr(count);
        index = 0;
        for (const auto &item : productInfoList) {

            auto &tempInfo = productInfoArr[index++];
            tempInfo.Int0 = item.Int0;
            tempInfo.Int1 = item.Int1;
            tempInfo.Int2 = item.Int2;
            tempInfo.Int4 = item.Int4;
            tempInfo.Int6 = item.Int6;

            constexpr size_t size_ID1 = sizeof(tempInfo.ID1) - 1;
            strncpy(tempInfo.ID1, item.ID1.c_str(), size_ID1);
            tempInfo.ID1[size_ID1] = '\0';

            constexpr size_t size_ID2 = sizeof(tempInfo.ID2) - 1;
            strncpy(tempInfo.ID2, item.ID2.c_str(), size_ID2);
            tempInfo.ID2[size_ID2] = '\0';

            constexpr size_t size_Str4 = sizeof(tempInfo.Str4) - 1;
            strncpy(tempInfo.Str4, item.Str4.c_str(), size_Str4);
            tempInfo.Str4[size_Str4] = '\0';

            constexpr size_t size_Str5 = sizeof(tempInfo.Str5) - 1;
            strncpy(tempInfo.Str5, item.Str5.c_str(), size_Str5);
            tempInfo.Str5[size_Str5] = '\0';

            constexpr size_t size_Str6 = sizeof(tempInfo.Str6) - 1;
            strncpy(tempInfo.Str6, item.Str6.c_str(), size_Str6);
            tempInfo.Str6[size_Str6] = '\0';

            constexpr size_t size_Time0 = sizeof(tempInfo.Time0) - 1;
            strncpy(tempInfo.Time0, item.Time0.c_str(), size_Time0);
            tempInfo.Time0[size_Time0] = '\0';
            // productInfoArr[index++] = tempInfo;
        }

        // 初始化数据库连接
        PasteDB pdb = PasteDB(connStr);
        pdb.ProductListInsert(productInfoArr, false);
    } catch (const std::exception &e) {
        PasteIO::WriteError("[ProductProcess]: " + std::string(e.what()));
    }
    return 0;
}

// 整合产品信息和图片信息
int PasteModule::ProductImgIntegration(const PasteDB::ConnStruct &connStr, const std::string &dir, const std::string &imgExt,
                                       const int baseNum, const int categoryID, const int materialID,
                                       const std::string &categoryName) {

    // 定义一个lambda函数，用于修改产品和图片信息
    // 图片中的尺寸质量保存到产品信息中
    // 修正产品信息类别号和关键字
    // 图片ID拷贝给产品信息
    // 关键字拷贝给图片信息
    auto changeProductAndImgInfo = [&categoryName, &categoryID](PasteDB::ProductStruct &productInfo,
                                                                PasteDB::ImageStruct &imgInfo) -> void {
        std::string tempStr6(productInfo.Str6);
        // imgInfo.Str1中存了大图名称_小图序号
        PasteConvert::AddString(tempStr6, imgInfo.Str1, false);

        if (imgInfo.Int1 > 0 && imgInfo.Int1 != productInfo.Int1) {
            if (productInfo.Int1 == 0) {
                productInfo.Int1 = imgInfo.Int1;
            } else {
                PasteConvert::AddString(tempStr6, "图尺寸1:" + imgInfo.Int1, false);
            }
        }
        if (imgInfo.Int2 > 0 && imgInfo.Int2 != productInfo.Int2) {
            if (productInfo.Int2 == 0) {
                productInfo.Int2 = imgInfo.Int2;
            } else {
                PasteConvert::AddString(tempStr6, "图尺寸2:" + imgInfo.Int2, false);
            }
        }
        if (imgInfo.Int4 > 0 && imgInfo.Int4 != productInfo.Int4) {
            if (productInfo.Int4 == 0) {
                productInfo.Int4 = imgInfo.Int4;
            } else {
                PasteConvert::AddString(tempStr6, "图质量:" + imgInfo.Int4, false);
            }
        }

        constexpr size_t size_Str6 = sizeof(productInfo.Str6) - 1;
        auto tempStr6Len = tempStr6.length();
        if (tempStr6Len > 250) {
            std::cout << tempStr6 << std::endl;
        }

        strncpy(productInfo.Str6, tempStr6.c_str(), size_Str6);

        productInfo.Str6[size_Str6] = '\0';

        // 如果产品信息中的类别ID与给定的类别ID不匹配
        if (productInfo.Int6 != categoryID) {
            std::cout << "类别不匹配,是否要修改类别ID为:" << categoryID << "  添加类别名:" << categoryName << std::endl;
            std::cout << "输入1修改类别,输入其他保留原始类别" << std::endl;
            int tempInt = 0;
            std::cin >> tempInt;
            if (tempInt == 1) {

                // 构建新的关键字字符串
                std::string testStr =
                    (productInfo.Int6 == 0) ? categoryName + productInfo.Str5 : categoryName + '_' + productInfo.Str5;

                // 更新类别号和关键字
                productInfo.Int6 = categoryID;

                // 定义缓冲区大小，确保不会超出字段长度
                constexpr size_t size_Str5 = sizeof(productInfo.Str5) - 1;
                // 复制新的关键字到产品信息中，确保末尾为空字符
                strncpy(productInfo.Str5, testStr.c_str(), size_Str5);
                productInfo.Str5[size_Str5] = '\0';

                // 输出更新后的类别号和关键字
                std::cout << "类别号改为:" << productInfo.Int6 << " 关键字改为:" << productInfo.Str5 << std::endl;
            }
        }
        // 把图片ID插入到产品信息中(字段大小相等无需截断处理)
        strcpy(productInfo.ID3, imgInfo.ID1);

        // 产品信息中的关键字插入到图片信息中
        strcpy(imgInfo.Str0, productInfo.Str5);
        imgInfo.Int6 = productInfo.Int6;
    };

    // 检查 OpenCV 是否正确初始化
    if (!cv::getBuildInformation().empty()) {
        std::cout << "OpenCV已正确初始化。" << std::endl;
    } else {
        std::cerr << "错误: OpenCV初始化失败。任意键退出程序!" << std::endl;
        std::cin.get();
        exit(0);
    }

    // 创建错误目录
    std::string errInt5Dir = dir + "errInt5/";
    PasteIO::VerifyDir(errInt5Dir, true);

    PasteDB pdb = PasteDB(connStr);                     // 初始化数据库连接
    std::vector<PasteDB::ImageStruct> imginfoArr;       // 初始化图片信息数组
    pdb.ImageListSelect(imginfoArr, categoryID, false); // 从数据库中获取图片信息

    std::stringstream errInt5ss; //  错误信息流

    int index = 0;
    for (auto &item : imginfoArr) {
        index++;

        int imgNum = baseNum - item.Int0;       // 图片原始编号
        PasteDB::ProductStruct productInfo;     // 产品信息
        pdb.ProductSelect(productInfo, imgNum); // 从数据库中查询与图片原始编号相同的产品信息

        // 产品信息和图片 类别ID 相同的直接拷贝 跳过
        if (productInfo.Int6 == item.Int6) {
            // 修改产品信息和图片信息
            changeProductAndImgInfo(productInfo, item);
            // 修改主表信息
            pdb.ProductUpdate(productInfo, true);
            // // 删除主表原始信息
            // pdb.ProductDelete(productInfo.Int0, true);
            // // 把更改后的信息插入主表
            // pdb.ProductInsert(productInfo, true);
            continue;
        }

        std::cout << std::endl;
        std::cout << "第" << index << "个图片编号:" << imgNum << "  位置:" << item.Str1 << std::endl;
        std::string path = dir + item.ID1 + imgExt; // 图片路径

        // 显示图片
        const cv::Mat &tempImg = cv::imread(path);
        if (tempImg.empty()) {
            std::cerr << "无法读取图片：" << path << std::endl;
            continue;
        }
        cv::imshow("PasteImg", tempImg);
        cv::waitKey(10);

        // 类别不同输出是啥原因
        if (productInfo.Int0 == 0) // 没有查询到信息
        {
            std::cout << "主表中没有查询到图片信息!" << std::endl;
        } else { // 产品信息和图片 类别ID 不同的
            std::cout << "查询到信息类别与图片不匹配:" << std::endl;
            std::cout << productInfo.Int0 << " 关键字:" << productInfo.Str5 << " 备注:" << productInfo.Str6
                      << " 已有图片ID:" << productInfo.ID3 << std::endl;
        }

        std::vector<PasteDB::ProductStruct> productInfo2Arr; // 在副表中查询产品信息列表
        pdb.Product2ListSelect(productInfo2Arr, imgNum);

        // 副表中查询到产品信息 让用户选择需要那条信息
        if (!productInfo2Arr.empty() && productInfo2Arr.size() > 0) {

            auto arrCount = productInfo2Arr.size();
            int arrIndex = 0;
            std::cout << "在副表中查询到 " << arrCount << " 条信息:" << std::endl;
            for (auto &item2 : productInfo2Arr) {
                arrIndex++;
                std::cout << arrIndex << " -> 编号:" << item2.Int0 << " 关键字:" << item2.Str5 << " 描述:" << item2.Str6
                          << std::endl;
            }
            std::cout << "请选择要把哪条信息插入主表中?(不在列表中的数字视为不选择任何一条):" << std::endl;
            int tempInt = 0;
            std::cin >> tempInt;
            // 如果用户选择插入
            if (tempInt > 0 && tempInt <= arrCount) {
                auto &productInfo2 = productInfo2Arr[tempInt - 1];

                // 更正产品信息和图片信息
                changeProductAndImgInfo(productInfo2, item);

                // 如果主表中有错误信息
                if (productInfo.Int0 != 0) {
                    // 将主表中的错误信息插入副表
                    pdb.ProductInsert(productInfo, false);
                    // 删除主表中的错误信息
                    pdb.ProductDelete(productInfo.Int0, true);
                }
                // 将副表中的信息插入主表
                pdb.ProductInsert(productInfo2, true);

                continue;
            }
        }

        // 主表类别不同的再问下要不要改为相同
        if (productInfo.Int0 != 0) {
            std::cout << "1:将[产品信息]表中的类别改为和[图片信息]一致" << std::endl;
            std::cout << "2:将[图片信息]表中的类别改为和[产品信息]一致" << std::endl;
            std::cout << "请选择要执行那个命令?(不在列表中的数字视为不选择任何一条):" << std::endl;
            int tempInt = 0;
            std::cin >> tempInt;
            if (tempInt == 1) {
                // 将主表中的错误信息插入副表
                pdb.ProductInsert(productInfo, false);

                // 更正产品信息和图片信息
                changeProductAndImgInfo(productInfo, item);

                // 修改主表信息
                pdb.ProductUpdate(productInfo, true);
                // // 删除主表错误信息
                // pdb.ProductDelete(productInfo.Int0, true);
                // // 把更改后的信息插入主表
                // pdb.ProductInsert(productInfo, true);

                continue;
            } else if (tempInt == 2) {
                // 更改图片类别ID
                item.Int6 = productInfo.Int6;
                // 更新图片信息的关键字
                strcpy(item.Str0, productInfo.Str5);
                // 把图片ID插入到产品信息中(字段大小相等无需截断处理)
                strcpy(productInfo.ID3, item.ID1);

                // 修改主表信息
                pdb.ProductUpdate(productInfo, true);
                // // 删除主表错误信息
                // pdb.ProductDelete(productInfo.Int0, true);
                // // 把更改后的信息插入主表
                // pdb.ProductInsert(productInfo, true);

                continue;
            }
        }
        // 是否要再主表中添加图片对应产品信息

        std::cout << "1:将[图片信息]表中的信息添加到[产品信息]中" << std::endl;
        int tempInt = 0;
        std::cin >> tempInt;
        if (tempInt == 1) {
            PasteDB::ProductStruct productInfo2;
            // 产品编号
            productInfo2.Int0 = imgNum;
            // 尺寸1
            productInfo2.Int1 = item.Int1;
            // 尺寸2
            productInfo2.Int2 = item.Int2;
            // 尺寸3
            productInfo2.Int3 = item.Int3;
            // 质量1
            productInfo2.Int4 = item.Int4;
            // 质量2
            productInfo2.Int5 = item.Int5;
            // 材质ID
            productInfo2.Int7 = item.Int7;

            // 时间
            strcpy(productInfo2.Time0, item.Time0);

            // 更正产品信息和图片信息
            changeProductAndImgInfo(productInfo2, item);

            // 如果主表中有错误信息
            if (productInfo.Int0 != 0) {
                // 将主表中的错误信息插入副表
                pdb.ProductInsert(productInfo, false);
                // 删除主表中的错误信息
                pdb.ProductDelete(productInfo.Int0, true);
            }

            // 把更改后的信息插入主表
            pdb.ProductInsert(productInfo2, true);

            continue;
        }

        // 都无可用信息的图片添加到错误文件中
        item.Int5 = 0;
        errInt5ss << item.ID1 << ",\t编号:" << imgNum << ",\t位置:" << item.Str1 << ",\t统一编号:" << std::hex << item.Int0
                  << std::dec << std::endl;
        PasteIO::MoveFileAndRename(dir + item.ID1 + imgExt, errInt5Dir + item.ID1 + imgExt);
    }
    cv::destroyAllWindows();
    PasteIO::SaveStringToFile(errInt5ss, errInt5Dir + "errInt5.txt");

    std::cout << "更新图片信息..." << std::endl;
    pdb.ImageListInsert(imginfoArr, true);
    std::cout << "更新完成" << std::endl;
    return 0;
}

/**
 * @brief 执行图像缩放操作
 *
 * 根据给定的目录和图像文件扩展名，查找并处理该目录下所有的对应图像文件。
 * 缩放操作的具体实现细节和逻辑处理过程需要根据实际代码内容进行分析。
 *
 * @param dir 图像文件所在的目录路径
 * @param imgExt 图像文件的扩展名，用于识别图像文件类型
 * @return int 缩放操作的结果，通常0表示成功，非0表示失败
 */
int PasteModule::ImgShrink(const std::string &dir, const std::string &imgExt) {
    // 检查 OpenCV 是否正确初始化
    if (!cv::getBuildInformation().empty()) {
        std::cout << "OpenCV已正确初始化。" << std::endl;
    } else {
        std::cerr << "错误: OpenCV初始化失败。任意键退出程序!" << std::endl;
        std::cin.get();
        exit(0);
    }

    // 提示用户将要处理的目录，并请求确认
    std::cout << "程序要对[" << dir << "]目录下的[" << imgExt << "]图片进行缩小操作!" << std::endl;
    std::cout << "确认请输入 1 或 y ,其键退出程序" << std::endl;
    std::string input;
    std::cin >> input;
    if (input != "1" && input != "y") {
        return 1;
    }

    // 获取指定目录下特定扩展名的文件
    std::vector<fs::path> files; // 图片信息文本列表
    try {
        PasteIO::GetFiles(files, dir, imgExt, false);
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
        PasteIO::WriteError(e.what());
    }

    // 定义保存图像的目录和扩展名
    std::string saveDir = dir + "shrink/"; // 图片保存目录
    // 尝试创建保存图像所需的目录
    try {
        PasteIO::VerifyDir(saveDir, true);
    } catch (const std::exception &e) {
        PasteIO::WriteError("[ImgShrink] " + std::string(e.what()));
    }

    // 创建线程池
    size_t threadCount = static_cast<size_t>(std::thread::hardware_concurrency());
    if (threadCount == 0) {
        threadCount = 1;
    }
    std::cout << "此处将采用多线程处理图像，使用线程数: " << threadCount << std::endl;
    std::vector<std::thread> threads;
    threads.reserve(threadCount);

    int index = 0;
    try {
        // 遍历文件列表，处理每个文件
        for (auto &p : files) {
            if (threads.size() >= threadCount) {
                std::cout << "线程已满，等待中..." << std::endl;
                for (auto &t : threads) {
                    if (t.joinable())
                        t.join();
                }
                threads.clear();
                std::cout << "这一轮全部处理完成" << std::endl;
            }

            std::cout << "缩小第 " << ++index << " 个图片" << std::endl;
            std::string destPath = saveDir + p.stem().string() + ".jpg";
            threads.emplace_back(PasteAdjustImg::ImgResize, p.string(), destPath);
        }
        // 等待剩余线程完成
        for (auto &t : threads) {
            if (t.joinable())
                t.join();
        }
        std::cout << "全部处理完成,共处理了 " << index << " 个图像" << std::endl;
    } catch (const std::exception &e) {
        PasteIO::WriteError("[ImgShrink] " + std::string(e.what()));
    } catch (...) {
        std::cerr << "图像处理过程中发生异常" << std::endl;
        PasteIO::WriteError("[ImgShrink] 图像处理过程中发生异常");
    }
    return 0;
}